package org.moonlight.jvm.classfile;

import lombok.Getter;
import org.moonlight.jvm.classfile.attribute.AttributeInfo;
import org.moonlight.jvm.classfile.attribute.debuginfo.SourceFileAttribute;
import org.moonlight.jvm.classfile.constant.StaticConstantPool;
import org.moonlight.jvm.common.Constant;

/**
 * 类文件结构
 *
 * Class文件是一组以8位字节为基础单位的二进制流，当遇到需要8位字节以上空间的数据项时，则会按照高位在前的方式（BigEnding - 大端序）分隔成若干个8位字节进行存储。
 * Class文件由无符号数和表构成:
 *   无符号数: 以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节和8个字节的无符号数，可以用来描述数字、索引引用、字面量、按照UTF-8编码构成的字符串值
 *       表: 由多个无符号数或其他表作为数据项构成的复杂数据类型，所有表都习惯性地以“_info”结尾。
 *
 * Class文件格式如下:
 *   类型	            描述	                                    备注
 *   u4	               magic	                                魔数：0xCAFEBABE
 *   u2	               minor_version	                        小版本号
 *   u2	               major_version	                        主版本号
 *   u2	               constant_pool_count	                    常量池大小，从1开始
 *   cp_info	       constant_pool[constant_pool_count - 1]	常量池信息
 *   u2	               access_flags	                            访问标志
 *   u2                this_class	                            当前类类名（符号引用）在静态常量池的索引下标
 *   u2	               super_class	                            父类类名（符号引用）在静态常量池的索引下标
 *   u2	               interfaces_count                     	接口个数
 *   u2	               interfaces[interfaces_count]	            当前类所实现的接口的接口名（符号引用）在静态常量池中的索引下标
 *   u2	               fields_count	                            字段数
 *   field_info	       fields[fields_count]	                    字段表信息
 *   u2	               methods_count	                        方法数
 *   method_info	   methods[methods_count]	                方法表信息
 *   u2	               attributes_count	                        属性个数
 *   attribute_info    attributes[attributes_count]	            属性表信息
 *
 * 直接引用: 拥有所需要数据的地址值，可以直接根据地址值获取到数据。
 * 符号引用: 符号引用是一个字符串，它给出了被引用的内容的名字并且可能会包含一些其他关于这个被引用项的信息——这些信息必须足以唯一的识别一个类、字段、方法。
 *         对于其他类的符号引用必须给出类的全限定名。对于其他类的字段，必须给出类名、字段名以及字段描述符。对于其他类的方法的引用必须给出类名、方法名以及方法的描述符。
 *
 *
 * @author Moonlight
 * @createTime 2023/8/14 17:50
 **/
@Getter
public class ClassFile {

    /** 次版本号 - 次版本号只在J2SE 1.2之前用过，从1.2开始基本上就没什么用了（都是0） **/
    private int minorVersion;
    /** 主版本号 -主版本号在J2SE 1.2之前是45，从1.2开始，每次有大的Java版本发布，都会加1 **/
    private int majorVersion;
    /** 静态常量池 - class文件 中的 常量池. 还没有被加载到 JVM 中 **/
    private final StaticConstantPool scp;
    /** 类访问修饰符 - 一个16位的“bitmask”. 指出class文件定义的是类还是接口或者枚举，注解，访问级别是public还是private，是否为抽象类或final 等等 **/
    private final int accessFlags;
    /** 当前类类名（符号引用）在静态常量池的索引下标 **/
    private final int thisClassIdx;
    /** 父类类名（符号引用）在静态常量池的索引下标 **/
    private final int supperClassIdx;
    /** 当前类所实现的接口的接口名（符号引用）在静态常量池中的索引下标 **/
    private final int[] interfaces;
    /** 字段信息 **/
    private final MemberInfo[] fields;
    /** 方法信息 **/
    private final MemberInfo[] methods;
    /** 属性信息 **/
    private final AttributeInfo[] attributes;

    public ClassFile(byte[] data) {
        ClassReader reader = new ClassReader(data);
        this.readAndCheckMagic(reader);
        this.readAndCheckVersion(reader);
        this.scp = new StaticConstantPool(reader);
        this.accessFlags = reader.readUint16();
        this.thisClassIdx = reader.readUint16();
        this.supperClassIdx = reader.readUint16();
        this.interfaces = reader.readUint16s();
        this.fields = MemberInfo.readMembers(reader, this.scp);
        this.methods = MemberInfo.readMembers(reader, this.scp);
        this.attributes = AttributeInfo.readAttributes(reader, this.scp);
    }

    /**
     * 读取并检查魔数是否为 CAFEBABE
     *
     * @param reader 类文件数据读取解析器
     * @createTime 8:43 2023/8/15
     * @author moonlight
     **/
    private void readAndCheckMagic(ClassReader reader) {
        long magic = reader.readUint32();
        if (magic != (Constant.CAFE_BABE & 0x0FFFFFFFFL)) {
            throw new ClassFormatError("Unrecognized Magic Number");
        }
    }

    /**
     * 读取并解析版本号
     *
     * @param reader 类文件数据读取解析器
     * @createTime 8:44 2023/8/15
     * @author moonlight
     **/
    private void readAndCheckVersion(ClassReader reader) {
        // 次版本号只在J2SE 1.2之前用过，从1.2开始基本上就没什么用了（都是0）
        this.minorVersion = reader.readUint16();
        // 主版本号在J2SE 1.2之前是45，从1.2开始，每次有大的Java版本发布，都会加1。
        this.majorVersion = reader.readUint16();
        // Java SE 8支持版本号为45.0～52.0的class文件
        switch (this.majorVersion) {
            case 45:
                return;
            case 46:
            case 47:
            case 48:
            case 49:
            case 50:
            case 51:
            case 52:
                if (this.minorVersion == 0) {
                    return;
                }
            default:
                throw new UnsupportedClassVersionError();
        }
    }

    public String getClassName() {
        return this.scp.getClassName(this.thisClassIdx);
    }

    public String getSuperClassName() {
        return this.scp.getClassName(this.supperClassIdx);
    }

    public String[] interfaceNames() {
        String[] names = new String[this.interfaces.length];
        for (int i = 0; i < this.interfaces.length; i++) {
            names[i] = this.scp.getClassName(this.interfaces[i]);
        }
        return names;
    }
    
    public SourceFileAttribute sourceFileAttribute() {
        for (AttributeInfo attr : attributes) {
            if (attr instanceof SourceFileAttribute) {
                return (SourceFileAttribute) attr;
            }
        }
        return null;
    }
}